home *** CD-ROM | disk | FTP | other *** search
/ Delphi 5 for Professionals / DELPHI5.iso / AddOns / Components / Orpheus v3.02 / SETUP.EXE / %MAINDIR% / Ovccache.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-02-25  |  17.9 KB  |  593 lines

  1. {*********************************************************}
  2. {*                  OVCCACHE.PAS 3.00                    *}
  3. {*     Copyright (c) TurboPower Software Co 1995-99      *}
  4. {*                 All rights reserved.                  *}
  5. {*********************************************************}
  6.  
  7. {$I OVC.INC}
  8.  
  9. {$B-} {Complete Boolean Evaluation}
  10. {$G+} {286 Instructions}
  11. {$I+} {Input/Output-Checking}
  12. {$N+} {Numeric Coprocessor}
  13. {$P+} {Open Parameters}
  14. {$Q-} {Overflow Checking}
  15. {$T-} {Typed @ Operator}
  16. {$W-} {Windows Stack Frame}
  17. {$X+} {Extended Syntax}
  18.  
  19. {$C MOVEABLE,DEMANDLOAD,DISCARDABLE}
  20.  
  21. unit OvcCache;
  22.   {general cache object}
  23.  
  24.  
  25.   (*******************************************************************
  26.  
  27.    TOvcCache is a simple data cache component that can be used is
  28.    situations where "normal" data retrieval methods would be too slow,
  29.    i.e., displaying records in a virtual ListBox or in a table
  30.    component. TOvcCache does not implement "write chaching", you must
  31.    provide a mechanism to write data once it has been modified and to
  32.    inform the cache so that it can refresh that record.
  33.  
  34.    To use a TOvcCache component in your application, you can either
  35.    create an instance of it dynamically (demonstrated in the CUSTORD
  36.    demo program) or by dropping it on a form (after registering the
  37.    component with Delphi so that it is available from the component
  38.    palette). In either case, you _MUST_ assign a method to the
  39.    OnGetItem event. The method assigned to OnGetItem is called
  40.    whenever the cache needs to fill or update its internal copy of a
  41.    cache element.
  42.  
  43.    Once this is setup, your application will always obtain data
  44.    through the cache. It, in turn, will call the method assigned to
  45.    the OnGetItem only when it has to. Use the Items indexed property
  46.    to obtain a pointer to a cache item, e.g., MyCache.Items[3] would
  47.    return a pointer to the fourth item (because the array is zero-
  48.    based) in the cache.
  49.  
  50.    The number of data items held in the cache is determined by the
  51.    MaxCacheItems property and must be at least 2 and no more than
  52.    MaxLongInt. The number of items you want the cache to maintain
  53.    depends on where that data is coming from, whether the data is
  54.    in a file that is shared by others, and most importantly, through
  55.    experimentation.
  56.  
  57.  
  58.    TOvcCache provide the following events, methods, and properties:
  59.    ===================================================================
  60.  
  61.    property CacheHits : LongInt
  62.  
  63.      CacheHits determines the number of times a requested item was in
  64.      the cache and did not require loading (by calling OnGetItem).
  65.  
  66.    property CacheMisses : LongInt
  67.  
  68.      CacheMisses determines the number of times a requested item was
  69.      not in the cache and required loading (by calling OnGetItem).
  70.  
  71.    procedure Clear;
  72.  
  73.      Removes all items currently in the cache and clears the locked
  74.      cache item flag (see LockCacheItem).
  75.  
  76.    property Count : LongInt
  77.  
  78.      Count is a read-only property that returns the number of items
  79.      currently managed by the cache.
  80.  
  81.    property Items[Index : LongInt] : Pointer (read-only)
  82.  
  83.      This index property is used to obtain a pointer to the data
  84.      managed by the cache object. The returned pointer usually
  85.      references a data record that has been allocated on the heap but,
  86.      it can also be a pointer to a class instance.
  87.  
  88.    procedure LockCacheItem(Index : LongInt);
  89.  
  90.      Locks the specified cache item so that it will not be purged
  91.      when the cache performs its search for an item to remove when
  92.      it needs to make room for a new item. Calling LockCacheItem
  93.      clears any previously locked item.
  94.  
  95.    property LockedItem : LongInt
  96.  
  97.      LockedItem is a read-only property that returns the index of the
  98.      currently locked cache item. If no cache item is locked,
  99.      LockedItem returns -1.
  100.  
  101.    property MemoryUsage : LongInt (read-only)
  102.  
  103.      MemoryUsage is a read-only property that returns the amount of
  104.      memory currently in use by the cache object and all of the cache
  105.      elements.
  106.  
  107.    property OnDoneItem : TOnDoneItemEvent
  108.    TOnDoneItemEvent = procedure(Index : LongInt; P : Pointer;
  109.                                 Size : Word) of object;
  110.  
  111.      If a method is assigned to this event, it is called when the
  112.      cache needs to remove one of its items. (P) is a pointer to the
  113.      cache item and Size is the size of the item that was set when
  114.      the item was added to the cache through a call to the method
  115.      assigned to the OnGetItem event. If you do not assign a method
  116.      to this event, the cache will attempt to deallocate Size bytes
  117.      of memory for (P).
  118.  
  119.    property OnGetItem : TOnGetItemEvent
  120.    TOnGetItemEvent = procedure(Index : LongInt; var P : Pointer;
  121.                                var Size : Word) of object;
  122.  
  123.      The method assigned to this event is called when the cache
  124.      receives a request for an item and that item is not in the
  125.      cache. You must assign a method to this event, otherwise the
  126.      cache has no way to obtain the requested data.
  127.  
  128.      Index is a number from 0 to MaxLongInt and can represent record
  129.      numbers corresponding to records in a data file or just about
  130.      anything you want them to. The cache assumes that you will
  131.      request items using the same index number that was used when the
  132.      item was added to the cache.
  133.  
  134.      (P) is a pointer to the global memory containing the data you
  135.      wish to add to the cache. You must allocate this memory on the
  136.      global heap rather than on the stack or as an address of a local
  137.      variable. If you specify the size of the memory allocation in
  138.      Size, the cache will deallocate the memory for this item when it
  139.      is no longer needed (if OnDoneItem is not assigned).
  140.  
  141.      (P) could also point to a class instance if you had an
  142.      application that required an object cache. If this is the case,
  143.      you must assign a method to the OnDoneItem event so that you can
  144.      destroy the objects contained within the cache. If you fail to do
  145.      this, the cache will attempt to dispose of Size bytes of memory
  146.      for P and will not properly destroy the object.
  147.  
  148.    procedure PreLoad(Index, Number : LongInt);
  149.  
  150.      PreLoad loads the specified Number of items starting with the
  151.      index specified by Index. Clear is called to remove any
  152.      existing cache items before the new items are added.
  153.  
  154.    procedure Refresh;
  155.  
  156.      Refresh re-loads all items currently in the cache. Calling
  157.      Refresh also clears the item locked by a previous call to
  158.      LockCacheItem.
  159.  
  160.    procedure UnlockCacheItem;
  161.  
  162.      Unlocks a cache item previously locked using LockCacheItem.
  163.  
  164.    procedure Update(Index : LongInt);
  165.  
  166.      Freshens the data for the specified Index cache item.
  167.  
  168.    property DiscardMethod : TDiscardMethod
  169.    TDiscardMethod = (dmMostDistant, dmLeastUsed);
  170.  
  171.      DiscardMethod determines which method is used to determine the
  172.      cache item to remove to make room for a new cache item.
  173.  
  174.      dmMostDistant is the default and involves a simple comparison of
  175.      the cache index numbers to determine which one is the most
  176.      distant from the index of the item currently being added. This
  177.      method is useful when the cache indexes represent sequential
  178.      record numbers.
  179.  
  180.      dmLeastUsed performs a search through all cache items, looking
  181.      for the item that has been retrieved the least number of times.
  182.  
  183.    property MaxCacheItems : LongInt
  184.  
  185.      MaxCacheItems determines the maximum number of items maintained
  186.      by the cache. The "right" value for this property is best
  187.      determined through experimentation.
  188.  
  189.  
  190.    ******************************************************************)
  191.  
  192. interface
  193.  
  194. uses
  195.   Classes;
  196.  
  197. type
  198.   TDiscardMethod = (dmMostDistant, dmLeastUsed);
  199.  
  200. const
  201.   DefDiscardMethod = dmMostDistant;
  202.   DefMaxCacheItems = 10;
  203.   DefMinCacheItems = 2;  {2 or greater}
  204.  
  205. type
  206.   {record for one cache item}
  207.   PCacheRecord = ^TCacheRecord;
  208.   TCacheRecord = record
  209.     Index  : LongInt; {index number}
  210.     Size   : Word;    {size of data}
  211.     Data   : Pointer; {pointer to data}
  212.     Hits   : LongInt; {number of times used}
  213.   end;
  214.  
  215. type
  216.   TOnGetItemEvent =
  217.     procedure(Index : LongInt; var P : Pointer; var Size : Word)
  218.     of object;
  219.   TOnDoneItemEvent =
  220.     procedure(Index : LongInt; P : Pointer; Size : Word)
  221.     of object;
  222.  
  223.   TOvcCache = class(TComponent)
  224.   {.Z+}
  225.   protected {private}
  226.     {instance variables}
  227.     FCacheHits     : LongInt;   {number of times requested item was in cache}
  228.     FCacheMisses   : LongInt;   {number of times requested item was not in cache}
  229.     FDiscardMethod : TDiscardMethod; {method used to free cache items}
  230.     FList          : TList;     {list of cached items}
  231.     FMaxCacheItems : LongInt;   {maximum items allowed in cache}
  232.     FLockedItem    : LongInt;   {item to be kept in the cache}
  233.  
  234.     {event instance variables}
  235.     FOnGetItem     : TOnGetItemEvent;  {must be assigned}
  236.     FOnDoneItem    : TOnDoneItemEvent; {optional}
  237.  
  238.     {event wrapper methods}
  239.     procedure DoOnGetItem(Index : LongInt; var P : Pointer; var Size : Word);
  240.       {-call FOnGetItem if assigned, otherwise return nil}
  241.     procedure DoOnDoneItem(Index : LongInt; var P : Pointer; Size : Word);
  242.       {-call FOnDoneItem if assigned, otherwise deallocate cache item}
  243.  
  244.     {property methods}
  245.     function GetCount : LongInt;
  246.       {-return the number of items in the cache}
  247.     function GetItem(Index : LongInt) : Pointer;
  248.       {-return pointer to data for Index}
  249.     function GetMemoryUsage : LongInt;
  250.       {-return the amount of memory used for items in the cache}
  251.     procedure SetMaxCacheItems(Value : LongInt);
  252.       {-set the maximum number of items to cache}
  253.  
  254.     {internal methods}
  255.     procedure ResetHits;
  256.       {-reset hit count for all cached items}
  257.  
  258.   public
  259.     constructor Create(AOwner : TComponent);
  260.       override;
  261.     destructor Destroy;
  262.       override;
  263.   {.Z-}
  264.  
  265.     {public methods}
  266.     procedure Clear;
  267.       {-remove all items from cache}
  268.     procedure LockCacheItem(Index : LongInt);
  269.       {-lock the Index item so it remains in the cache}
  270.     procedure PreLoad(Index, Number : LongInt);
  271.       {-load Number items starting at Index}
  272.     procedure Refresh;
  273.       {-reload all items currently in cache}
  274.     procedure UnlockCacheItem;
  275.       {-unlock the previously locked item}
  276.     procedure Update(Index : LongInt);
  277.       {-reload data for the Index item}
  278.  
  279.     {public properties}
  280.     property CacheHits : LongInt
  281.       read FCacheHits write FCacheHits;
  282.     property CacheMisses : LongInt
  283.       read FCacheMisses write FCacheMisses;
  284.     property Count : LongInt
  285.       read GetCount;
  286.     property Items[Index : LongInt] : Pointer
  287.       read GetItem;
  288.     property LockedItem : LongInt
  289.       read FLockedItem;
  290.     property MemoryUsage : LongInt
  291.       read GetMemoryUsage;
  292.  
  293.   published
  294.     {published properties}
  295.     property DiscardMethod : TDiscardMethod
  296.       read FDiscardMethod write FDiscardMethod;
  297.     property MaxCacheItems : LongInt
  298.       read FMaxCacheItems write SetMaxCacheItems;
  299.  
  300.     {published events}
  301.     property OnDoneItem : TOnDoneItemEvent
  302.       read FOnDoneItem write FOnDoneItem;
  303.     property OnGetItem : TOnGetItemEvent
  304.       read FOnGetItem write FOnGetItem;
  305.   end;
  306.  
  307.  
  308. procedure Register;
  309.   {-IDE Component registration}
  310.  
  311.  
  312. implementation
  313.  
  314. {*** TOvcCache ***}
  315.  
  316. procedure TOvcCache.Clear;
  317.   {-remove all items from cache}
  318. var
  319.   I : LongInt;
  320.   P : PCacheRecord;
  321. begin
  322.   UnlockCacheItem; {clear locked item}
  323.   for I := 0 to FList.Count-1 do begin
  324.     P := PCacheRecord(FList.Items[I]);
  325.     DoOnDoneItem(P^.Index, P^.Data, P^.Size);
  326.     FreeMem(P, SizeOf(TCacheRecord));
  327.   end;
  328.   FList.Clear;
  329. end;
  330.  
  331. constructor TOvcCache.Create(AOwner : TComponent);
  332.   {-create cache with MaxItems maximum items}
  333. begin
  334.   inherited Create(AOwner);
  335.   MaxCacheItems := DefMaxCacheItems;
  336.   FList := TList.Create;
  337.  
  338.   FDiscardMethod := DefDiscardMethod;
  339.   FLockedItem := -1; {no locked item}
  340. end;
  341.  
  342. destructor TOvcCache.Destroy;
  343.   {-destroy cache object}
  344. begin
  345.   Clear;
  346.   FList.Free;
  347.   inherited Destroy;
  348. end;
  349.  
  350. procedure TOvcCache.DoOnGetItem(Index : LongInt; var P : Pointer; var Size : Word);
  351.   {-call FOnGetItem if assigned, otherwise return nil}
  352. begin
  353.   P := nil;
  354.   Size := 0;
  355.   if Assigned(FOnGetItem) then
  356.     FOnGetItem(Index, P, Size);
  357. end;
  358.  
  359. procedure TOvcCache.DoOnDoneItem(Index : LongInt; var P : Pointer; Size : Word);
  360.   {-call FOnDoneItem if assigned, otherwise deallocate cache item}
  361. begin
  362.   if Assigned(FOnDoneItem) then
  363.     FOnDoneItem(Index, P, Size)
  364.   else begin
  365.     FreeMem(P, Size);
  366.     P := nil;
  367.   end;
  368. end;
  369.  
  370. function TOvcCache.GetCount : LongInt;
  371.   {-return the number of items in the cache}
  372. begin
  373.   Result := FList.Count;
  374. end;
  375.  
  376. function TOvcCache.GetItem(Index : LongInt) : Pointer;
  377.   {-return pointer to data for the Index cache item}
  378. var
  379.   I   : LongInt;
  380.   P   : PCacheRecord;
  381.   DP  : Pointer;
  382.   SZ  : Word;
  383.   Idx : LongInt;
  384.  
  385.   function FindMostDistant : LongInt;
  386.   var
  387.     I        : LongInt;
  388.     Distance : LongInt;
  389.     P        : PCacheRecord;
  390.   begin
  391.     Distance := 0;
  392.     Result := -1;
  393.     for I := 0 to FList.Count-1 do begin
  394.       P := PCacheRecord(FList.Items[I]);
  395.       if (Abs(P^.Index - Index) > Distance) and
  396.          (P^.Index <> LockedItem) then begin
  397.         Distance := Abs(P^.Index - Index);
  398.         Result := I;
  399.       end;
  400.     end;
  401.   end;
  402.  
  403.   function FindLeastUsed : LongInt;
  404.   var
  405.     I    : LongInt;
  406.     Hits : LongInt;
  407.     P    : PCacheRecord;
  408.   begin
  409.     Hits := MaxLongInt;
  410.     Result := -1;
  411.     for I := 0 to FList.Count-1 do begin
  412.       P := PCacheRecord(FList.Items[I]);
  413.       if (P^.Hits < Hits) and (P^.Index <> LockedItem) then begin
  414.         Hits := P^.Hits;
  415.         Result := I;
  416.       end;
  417.     end;
  418.   end;
  419.  
  420. begin
  421.   Result := nil;
  422.  
  423.   {search for Index item in the cache}
  424.   for I := 0 to FList.Count-1 do begin
  425.     P := PCacheRecord(FList.Items[I]);
  426.     if Assigned(P) and (P^.Index = Index) then begin
  427.       {return pointer to cache data}
  428.       Result := P^.Data;
  429.       Inc(FCacheHits);
  430.       Inc(P^.Hits);
  431.       if P^.Hits < 0 then  {roll-over, clear all}
  432.         ResetHits;
  433.       Break;
  434.     end;
  435.   end;
  436.  
  437.   {see if we failed to find a match}
  438.   if not Assigned(Result) then begin
  439.     Inc(FCacheMisses);
  440.  
  441.     {ask for data for this cache item}
  442.     DoOnGetItem(Index, DP, SZ);
  443.     {exit if no data associated with this item}
  444.     if not Assigned(DP) then Exit;
  445.  
  446.     {if cache is full, discard one item in the list}
  447.     if FList.Count >= MaxCacheItems then begin
  448.  
  449.       if DiscardMethod = dmMostDistant then
  450.         Idx := FindMostDistant
  451.       else
  452.         Idx := FindLeastUsed;
  453.  
  454.       P := nil;
  455.       if Idx > -1 then begin
  456.         P := PCacheRecord(FList.Items[Idx]);
  457.         if Assigned(P) then begin
  458.           with P^ do
  459.             DoOnDoneItem(Index, Data, Size);
  460.  
  461.           {replace with new data}
  462.           P^.Index := Index;
  463.           P^.Data := DP;
  464.           P^.Size := SZ;
  465.           P^.Hits := 0;
  466.         end;
  467.       end;
  468.     end else begin
  469.       {otherwise, allocate a cache record (P)}
  470.       GetMem(P, SizeOf(TCacheRecord));
  471.  
  472.       {insert new cache record at top of list}
  473.       P^.Index := Index;
  474.       P^.Data := DP;
  475.       P^.Size := SZ;
  476.       P^.Hits := 0;
  477.       FList.Insert(0, P);
  478.     end;
  479.  
  480.     {return pointer to data in cache}
  481.     if Assigned(P) then
  482.       Result := P^.Data;
  483.   end;
  484. end;
  485.  
  486. function TOvcCache.GetMemoryUsage : LongInt;
  487.   {-return the amount of memory used for items in the cache}
  488. var
  489.   I : LongInt;
  490. begin
  491.   Result := SizeOf(TCacheRecord) * (FList.Count-1);
  492.   for I := 0 to FList.Count-1 do
  493.     Result := Result + PCacheRecord(FList.Items[I])^.Size;
  494. end;
  495.  
  496. procedure TOvcCache.LockCacheItem(Index : LongInt);
  497.   {-lock the Index item so it remaines in the cache}
  498. begin
  499.   FLockedItem := Index;
  500. end;
  501.  
  502. procedure TOvcCache.PreLoad(Index, Number : LongInt);
  503.   {-load Number items starting at Index}
  504. var
  505.   I : LongInt;
  506.   P : PCacheRecord;
  507. begin
  508.   {remove any existing cache items}
  509.   Clear;
  510.   {fill cache with Number items starting at Index}
  511.   for I := Index to Index+Number-1 do begin
  512.     {allocate a cache record (P)}
  513.     GetMem(P, SizeOf(TCacheRecord));
  514.     {ask for data for this item}
  515.     DoOnGetItem(I, P^.Data, P^.Size);
  516.     {exit if no data associated with this index}
  517.     if Assigned(P) then begin
  518.       P^.Index := I;
  519.       P^.Hits := 0;
  520.       {add to cache list}
  521.       FList.Add(P);
  522.     end;
  523.   end;
  524. end;
  525.  
  526. procedure TOvcCache.Refresh;
  527.   {-reload all items currently in cache}
  528. var
  529.   I : LongInt;
  530.   P : PCacheRecord;
  531. begin
  532.   UnlockCacheItem; {clear locked item}
  533.   for I := 0 to FList.Count-1 do begin
  534.     P := PCacheRecord(FList.Items[I]);
  535.     with P^ do begin
  536.       DoOnDoneItem(Index, Data, Size);
  537.       DoOnGetItem(Index, Data, Size);
  538.       Hits := 0;
  539.     end;
  540.   end;
  541. end;
  542.  
  543. procedure TOvcCache.ResetHits;
  544.   {-reset hit count for all cached items}
  545. var
  546.   I : LongInt;
  547. begin
  548.   for I := 0 to FList.Count-1 do
  549.     PCacheRecord(FList.Items[I])^.Hits := 0;
  550. end;
  551.  
  552. procedure TOvcCache.SetMaxCacheItems(Value : LongInt);
  553.   {-set the maximum number of items to cache}
  554. begin
  555.   FMaxCacheItems := Value;
  556.   if FMaxCacheItems < DefMinCacheItems then
  557.     FMaxCacheItems := DefMinCacheItems;
  558. end;
  559.  
  560. procedure TOvcCache.UnlockCacheItem;
  561.   {-unlock the previously locked item}
  562. begin
  563.   FLockedItem := -1;
  564. end;
  565.  
  566. procedure TOvcCache.Update(Index : LongInt);
  567.   {-reload data for Index}
  568. var
  569.   I : LongInt;
  570.   P : PCacheRecord;
  571. begin
  572.   for I := 0 to FList.Count-1 do begin
  573.     P := PCacheRecord(FList.Items[I]);
  574.     if P^.Index = Index then begin
  575.       with P^ do begin
  576.         DoOnDoneItem(Index, Data, Size);
  577.         DoOnGetItem(Index, Data, Size);
  578.         Hits := 0;
  579.         Break;
  580.       end;
  581.     end;
  582.   end;
  583. end;
  584.  
  585.  
  586.  
  587. procedure Register;
  588. begin
  589.   RegisterComponents('Orpheus (NV)', [TOvcCache]);
  590. end;
  591.  
  592.  
  593. end.